\documentclass[12pt]{report}
\usepackage{geometry}
\geometry{a4paper,scale=0.8}
\usepackage{longtable}
\usepackage{url}
\usepackage{listings}
\usepackage{graphicx}
\usepackage{float}
\usepackage{titlesec}
\usepackage{CJK}
\begin{CJK}{UTF8}{gkai}
\titleformat{\chapter}[hang]{\centering\Huge\bfseries}{\chaptername}{1em}{}
%\renewcommand{\chaptername}{第\CJKnumber{thechapter}章}
\title{A005 创建和读写MINIX1.0文件系统}
\author{Chuzy}
\date{2020/05/03}

\lstset{
    basicstyle          =   \small\ttfamily,          % 基本代码风格
    keywordstyle        =   \bfseries,          % 关键字风格
    commentstyle        =   \rmfamily\itshape,  % 注释的风格，斜体
    stringstyle         =   \ttfamily,  % 字符串风格
    flexiblecolumns,                % 别问为什么，加上这个
    numbers             =   left,   % 行号的位置在左边
    showspaces          =   false,  % 是否显示空格，显示了有点乱，所以不现实了
    numberstyle         =   \ttfamily,    % 行号的样式，小五号，tt等宽字体
    showstringspaces    =   false,
    captionpos          =   t,      % 这段代码的名字所呈现的位置，t指的是top上面
    frame               =   lrtb,   % 显示边框
    columns         =   flexible,     
    breaklines      =   true,
    language        =  [ANSI]C,
    escapeinside=``
}


%以上部分叫做"导言区",下面才开始写正文
\begin{document}

%先插入标题
\maketitle
\tableofcontents
\newpage 
\chapter{概述}
在004版本中，我们是通过手工改写硬盘映像内容的方式得到了一个安装了MINIX1.0的文件系统的硬盘映像，在本章，我们要编写一个能够创建和读写MINIX1.0文件系统的程序，并通过这个程序向硬盘文件中读写文件。
\chapter{流程图}
\begin{longtable}{|l|l|p{5cm}|p{3cm}|} 
\caption{0.003版本的文件}  
\label{tab: 0.004 files} \\ 
\hline 目录&文件名&作用&变化 \\
\hline / & m2.c & 程序入口 & 新增\\
\hline / & buffer.c & buffer机制 & 移植\\
\hline / & hd.c & 硬盘管理 & 移植\\
\hline / & block\_dev.c & 块设备驱动 & 移植\\
\hline / & super.c & super block操作 & 移植\\
\hline / & inode.c & inode操作 & 移植\\
\hline / & bitmap.c & inode和block位图操作 & 移植\\
\hline / & namei.c & 文件和目录相关inode操作 & 移植\\
\hline / & open.c & 文件打开和创建等 & 移植\\
\hline / & read\_write.c & 通用读写函数 & 移植\\
\hline / & file\_dev.c & 文件驱动 & 移植\\
\hline / & printk.c & 沿用linux内核打印 & 移植\\
\hline / & panic.c & 沿用linux内核异常处理 & 移植\\
\hline / & buffer.h & 头文件 & 移植\\ 
\hline / & d1.img & 磁盘镜像文件 & 新建\\
\hline / & Makefile & 增加了新的内核文件 & 新建\\
\hline  
\end{longtable}  

为了提高灵活性，我们主要分为两个场景。场景1, 磁盘镜像文件完成了格式化，但还没有安装文件系统；场景2, 磁盘镜像文件上有了文件系统，我们需要在上面创建目录、设备文件、复制本地文件到磁盘镜像的操作。如果是制作文件系统，我们要做的事情是：\\
1. 读入要制作文件系统的磁盘映像\\
2. 读入分区表，定位分区\\
3. 写入Super Block\\\\
如果是要把复制文件，我们要做的事情是：\\
1. 读入的磁盘映像\\
2. 读入分区表，定位分区\\
3. 读入Super Block\\
4. 读入inode\\
5. 定位目标路径\\
6. 如果目标路径不存在，创建目标路径\\
7. 如果目标文件已存在，覆盖目标文件\\
8. 复制文件内容
\chapter{功能入口源码m2.c}
\section{main函数}
作为总的入口函数，根据不同的命令字调用相应函数完成处理。
\begin{lstlisting}
//`为了尽量减少对linux原有代码修改而保留，程序中没有使用task相关结构`
struct task_struct init_task;
struct task_struct *current = &init_task, *last_task_used_math = NULL;

//`等以后再完善...`
void usage(void)
{
	printk("Usage: M1 0/1 diskimg\r\n");
	panic("0: read; 1 install");
}

int main(int argc, char **argv)
{
        if (argc != 3 && argc != 4)
                usage();

        buffer_init(); //`初始化buffer`
        hd_init(argv); //`读入硬盘镜像文件`

        switch(argv[1][0]){
                case '0': //`读入硬盘镜像上已经安装的文件系统`
                        printk("Read system\r\n");
                        sys_setup();
                        break;
                case '1': //`在读入的硬盘镜像上构建supper block`
                        printk("Write supper\r\n");
                        sys_install();
                        break;
                case '2': //`列出硬盘镜像上的目录`
                        printk("List dir\r\n");
                        sys_ls(argv[3]);
                        break;
                case '3': //`在硬盘镜像上创建/dev目录`
                        printk("Add /dev in root\r\n");
                        sys_adddir();
                        break;
                case '4': //`在硬盘镜像上创建/dev/tty0字符终端设备文件`
                        printk("Add /dev/tty0 in root\r\n");
                        sys_setup();
                        sys_mknod("dev/tty0",I_CHAR_SPECIAL,0x400);
                        break;
                case '5': //`复制文件到硬盘镜像上的指定目录`
                        printk("Write files in dir\r\n");
                        sys_writefile(argv[3]);
                        break;
                case '6'://`读出在硬盘镜像上指定目录的文件`
                        printk("Read files in dir\r\n");
                        sys_readfile(argv[3]);
                        break;

                default:
                        printk("flag error 0x%x\r\n", argv[1][0]);
                        usage();
        }
        return 0;
}

\end{lstlisting}
\chapter{文件高速缓存源码buffer.c}
buffer是linux用于硬盘操作的高速缓冲机制，我们直接移植了这个机制。
\section{全局变量}
\begin{lstlisting}[breaklines]
#include "buffer.h"

char end[1024*1024]; `为buffer分配内存空间，我们预留1024个block`
struct buffer_head * start_buffer = end;
unsigned long BUFFER_END = end + 1024*1024;

struct buffer_head * hash_table[NR_HASH];
static struct buffer_head * free_list;
static struct task_struct * buffer_wait = NULL;
int NR_BUFFERS = 0;
\end{lstlisting}
\section{wait\_on\_buffer函数}
我们没有等待buffer的操作，直接将lock状态清零；
\begin{lstlisting}[breaklines]
static inline void wait_on_buffer(struct buffer_head * bh)
{
        bh->b_lock = 0;
}
\end{lstlisting}
\section{sys\_sync函数}
这个函数直接移植。首先将inode的变化同步到buffer block中，然后同步所有buffer block到设备中。
\begin{lstlisting}[breaklines]
int sys_sync(void)
{
        int i;
        struct buffer_head * bh;

        sync_inodes();          /* write out inodes into buffers */
        bh = start_buffer;
        for (i=0 ; i<NR_BUFFERS ; i++,bh++) {
                wait_on_buffer(bh);
                if (bh->b_dirt)
                        ll_rw_block(WRITE,bh);
        }
        return 0;
}
\end{lstlisting}
\section{sync\_dev函数}
这个函数直接移植，我们用不到这个函数。
\begin{lstlisting}[breaklines]
static int sync_dev(int dev)
{
        int i;
        struct buffer_head * bh;

        bh = start_buffer;
        for (i=0 ; i<NR_BUFFERS ; i++,bh++) {
                if (bh->b_dev != dev)
                        continue;
                wait_on_buffer(bh);
                if (bh->b_dirt)
                        ll_rw_block(WRITE,bh);
        }
        return 0;
}

\end{lstlisting}
\section{remove\_from\_queues函数}
这个函数直接移植。首先将buffer head从hash队列中移除；然后将buffer head从freelist中移除。
\begin{lstlisting}[breaklines]
#define _hashfn(dev,block) (((unsigned)(dev^block))%NR_HASH)
#define hash(dev,block) hash_table[_hashfn(dev,block)]

static inline void remove_from_queues(struct buffer_head * bh)
{
/* remove from hash-queue */
        if (bh->b_next)
                bh->b_next->b_prev = bh->b_prev;
        if (bh->b_prev)
                bh->b_prev->b_next = bh->b_next;
        if (hash(bh->b_dev,bh->b_blocknr) == bh)
                hash(bh->b_dev,bh->b_blocknr) = bh->b_next;
/* remove from free list */
        if (!(bh->b_prev_free) || !(bh->b_next_free))
                panic("Free block list corrupted");
        bh->b_prev_free->b_next_free = bh->b_next_free;
        bh->b_next_free->b_prev_free = bh->b_prev_free;
        if (free_list == bh)
                free_list = bh->b_next_free;
}
\end{lstlisting}
\section{insert\_into\_queues函数}
这个函数直接移植。首先把buffer head节点放到free list的队尾；然后buffer head节点插入到hash表的最前面；原来代码中最后一条语句前应增加一条(b\_next不为null的判断)。
\begin{lstlisting}[breaklines]
static inline void insert_into_queues(struct buffer_head * bh)
{
/* put at end of free list */
        bh->b_next_free = free_list;
        bh->b_prev_free = free_list->b_prev_free;
        free_list->b_prev_free->b_next_free = bh;
        free_list->b_prev_free = bh;
/* put the buffer in new hash-queue if it has a device */
        bh->b_prev = NULL;
        bh->b_next = NULL;
        if (!bh->b_dev)
                return;
        bh->b_next = hash(bh->b_dev,bh->b_blocknr);
        hash(bh->b_dev,bh->b_blocknr) = bh;
        if(bh->b_next)//`这里需要增加条件，否则会异常`
                bh->b_next->b_prev = bh;
}

\end{lstlisting}
\section{find\_buffer函数}
这个函数直接移植。首先计算hash值，根据hash值看对应的buffer head队列是否为空；队列为空意味着该block没有buffer过，直接返回；队列不为空，则逐项检查队列中哪一个buffer block是要查找的block。
\begin{lstlisting}[breaklines]
static struct buffer_head * find_buffer(int dev, int block)
{               
        struct buffer_head * tmp;

        for (tmp = hash(dev,block) ; tmp != NULL ; tmp = tmp->b_next)
                if (tmp->b_dev==dev && tmp->b_blocknr==block)
                        return tmp;
        return NULL;
}
\end{lstlisting}
\section{get\_hash\_table函数}
这个函数直接移植。如果没有找到已经存在的buffer block，则直接返回null；如果找到，该block的引用次数+1, 并等待获得锁；获得锁后再次检查是否还是对应的buffer block，如对应则返回；否则释放对应的buffer block，重新检查；
\begin{lstlisting}[breaklines]
struct buffer_head * get_hash_table(int dev, int block)
{
        struct buffer_head * bh;

repeat:
        if (!(bh=find_buffer(dev,block)))
                return NULL;
        bh->b_count++;
        wait_on_buffer(bh);
        if (bh->b_dev != dev || bh->b_blocknr != block) {
                brelse(bh);
                goto repeat;
        }
        return bh;
}
\end{lstlisting}
\section{getblk函数}
这个函数直接移植。如果在hash表中找到对应的buffer block，则直接返回；否则从free\_list头部开始尝试获得一个空闲节点，如果节点的b\_count为0,则再等待获得锁之后，再进行一次检查，若还等于0,则确认找到一个节点；将找到的节点的count数+1, 然后移出空闲队列；若数据标志为脏，则获取锁，并尝试将数据同步到设备中；再检查一遍是否有人使用了此节点，若有，则放弃，重新尝试；若一切正常，将此节点插入hash队列中。可见，大量的处理在于冲突保护，而我们现在不需要考虑这些问题。
\begin{lstlisting}[breaklines]
struct buffer_head * getblk(int dev,int block)
{
        struct buffer_head * tmp;

repeat:
        if (tmp=get_hash_table(dev,block))
                return tmp;
        tmp = free_list;
        do {
                if (!tmp->b_count) {
                        wait_on_buffer(tmp);    /* we still have to wait */
                        if (!tmp->b_count)      /* on it, it might be dirty */
                                break;
                }
                tmp = tmp->b_next_free;
        } while (tmp != free_list || (tmp=NULL));
        /* Kids, don't try THIS at home ^^^^^. Magic */
        if (!tmp) {
                printk("Sleeping on free buffer ..");
                sleep_on(&buffer_wait);
                printk("ok\n");
                goto repeat;
        }
        tmp->b_count++;
        remove_from_queues(tmp);
        if (tmp->b_dirt)
                sync_dev(tmp->b_dev);
/* update buffer contents */
        tmp->b_dev=dev;
        tmp->b_blocknr=block;
        tmp->b_dirt=0;
        tmp->b_uptodate=0;
/* NOTE!! While we possibly slept in sync_dev(), somebody else might have
 * added "this" block already, so check for that. Thank God for goto's.
 */
        if (find_buffer(dev,block)) {
                tmp->b_dev=0;           /* ok, someone else has beaten us */
                tmp->b_blocknr=0;       /* to it - free this block and */
                tmp->b_count=0;         /* try again */
                insert_into_queues(tmp);
                goto repeat;
        }
        insert_into_queues(tmp);
        return tmp;
}
\end{lstlisting}
\section{brelse函数}
这个函数直接移植。首先等待该buffer block解锁；然后将该buffer block使用计数--；
唤醒等待空闲buffer block的进程；
\begin{lstlisting}[breaklines]
void brelse(struct buffer_head * buf)
{
        if (!buf)
                return;
        wait_on_buffer(buf);
        if (!(buf->b_count--))
                panic("Trying to free free buffer");
        wake_up(&buffer_wait);
}
\end{lstlisting}
\section{bread函数}
这个函数直接移植。根据要求读的dev和block，找到对应的buffer block\_head； 如果buffer中的block内容是最新的，直接返回buffer block中的内容；否则调用底层读block函数；如果buffer中的block内容更新成功，则返回buffer block中的内容，否则读取失败，释放对应的buffer block。
\begin{lstlisting}[breaklines]
struct buffer_head * bread(int dev,int block)
{
        struct buffer_head * bh;

        if (!(bh=getblk(dev,block)))
                panic("bread: getblk returned NULL\n");
        if (bh->b_uptodate)
                return bh;
        ll_rw_block(READ,bh);
        if (bh->b_uptodate)
                return bh;
        brelse(bh);
        return (NULL);
}
\end{lstlisting}
\chapter{硬盘驱动程序源码hd.c}
\section{全局变量}
\begin{lstlisting}[breaklines]
#include "buffer.h"
#include <stdio.h> //`用于打开磁盘镜像文件，这是gcc的头文件`
#include <fcntl.h> //`用于打开磁盘镜像文件，这是gcc的头文件`

/*
 * This code handles all hd-interrupts, and read/write requests to
 * the hard-disk. It is relatively straigthforward (not obvious maybe,
 * but interrupts never are), while still being efficient, and never
 * disabling interrupts (except to overcome possible race-condition).
 * The elevator block-seek algorithm doesn't need to disable interrupts
 * due to clever programming.
 */

/* Max read/write errors/sector */
#define MAX_ERRORS      5
#define MAX_HD          2
#define NR_REQUEST      32
#define HD_TYPE { 5,17,980,300,980,0 } //`写死的磁盘型号`
extern struct super_block super_block[];
extern struct task_struct * current;
/*
 *  This struct defines the HD's and their types.
 *  Currently defined for CP3044's, ie a modified
 *  type 17.
 */
static struct hd_i_struct{
        int head,sect,cyl,wpcom,lzone,ctl;
        } hd_info[]= { HD_TYPE };

#define NR_HD ((sizeof (hd_info))/(sizeof (struct hd_i_struct)))
int lHD[NR_HD];

struct hd_struct {
        long start_sect;
        long nr_sects;
} hd[5*MAX_HD]={{0,0},};

extern struct buffer_head * start_buffer;
\end{lstlisting}
\section{look\_buffer函数}
这个函数直接移植，我们实际上用不到对块进行锁操作。
\begin{lstlisting}[breaklines]

static inline void lock_buffer(struct buffer_head * bh)
{
	if (bh->b_lock)
		printk("hd.c: buffer multiply locked\n");
	bh->b_lock=1;
}

static inline void unlock_buffer(struct buffer_head * bh)
{
	if (!bh->b_lock)
		printk("hd.c: free buffer being unlocked\n");
	bh->b_lock=0;
	wake_up(&bh->b_wait);
}

static inline void wait_on_buffer(struct buffer_head * bh)
{
	cli();
	while (bh->b_lock)
		sleep_on(&bh->b_wait);
	sti();
}
\end{lstlisting}
\section{rw\_hd函数}
对于磁盘镜像文件，我们可以直接读取逻辑扇区号，而不再需要转换到磁头/柱面/扇区，因此需要重写这个函数。
\begin{lstlisting}[breaklines]
void rw_hd(int rw, struct buffer_head * bh)
{
        unsigned int block,dev;

        block = bh->b_blocknr << 1; //`一个buffer block缓存2个sector的数据`
        dev = MINOR(bh->b_dev); //`磁盘设备的Minor部分代表分区`
        if (dev >= 5*NR_HD || block+2 > hd[dev].nr_sects)
                return;
        block += hd[dev].start_sect;
        dev /= 5; //`一个磁盘连同其4个分区，作为5个设备`
        rw_logical_hd(rw, dev, block+1, bh); //`扇区号从1开始编号`
}
\end{lstlisting}
\section{dump\_partition函数}
这是我们新加的函数，用于打印出分区表，便于检查核对。
\begin{lstlisting}[breaklines]
void dump_partition(struct partition *p)
{
      printk("boot_ind 0x%x, head 0x%x.\n\r",(p-4)->boot_ind, (p-4)->head);
      printk("sector 0x%x, cyl 0x%x.\n\r",(p-4)->sector, (p-4)->cyl);
      printk("sys_ind 0x%x, end_head 0x%x.\n\r",(p-4)->sys_ind, (p-4)->end_head);
      printk("end_sector 0x%x, end_cyl 0x%x.\n\r",(p-4)->end_sector, (p-4)->end_cyl);
      printk("Start sec 0x%x, nr sects 0x%x.\n\r",(p-4)->start_sect ,(p-4)->nr_sects);
}
\end{lstlisting}
\section{sys\_setup函数}
这个函数直接移植。遍历所有硬盘，读取每块硬盘的bootSector；对于每块硬盘，遍历硬盘上的所有分区表，将分区表内容记录到hd结构中；最后安装root文件系统。这个函数中，我们唯一的改动是增加了一个分区表打印语句。
\begin{lstlisting}[breaklines]
/* This may be used only once, enforced by 'static int callable' */
int sys_setup(void)
{
	static int callable = 1;
	int i,drive;
	struct partition *p;

	if (!callable)
		return -1;
	callable = 0;
	for (drive=0 ; drive<NR_HD ; drive++) {
		rw_abs_hd(READ,drive,1,0,0,(struct buffer_head *) start_buffer);
		if (!start_buffer->b_uptodate) {
			printk("Unable to read partition table of drive %d\n\r",
				drive);
			panic("");
		}
		if (start_buffer->b_data[510] != 0x55 || (unsigned char)
		    start_buffer->b_data[511] != 0xAA) {
			printk("Bad partition table on drive %d\n\r",drive);
			panic("");
		}
		p = 0x1BE + (void *)start_buffer->b_data;
		for (i=1;i<5;i++,p++) {
			hd[i+5*drive].start_sect = p->start_sect;
			hd[i+5*drive].nr_sects = p->nr_sects;
		}
	}
	printk("Partition table%s ok.\n\r",(NR_HD>1)?"s":"");
	dump_partition(p); //`我们增加了一个分区表输出`
	mount_root();
	return (0);
}
\end{lstlisting}
\section{rw\_abs\_hd函数}
我们不再需要通过中断去读取硬盘，因此也不需要构造中断请求，只需要计算出逻辑扇区号，因此需要重写这个函数。
\begin{lstlisting}[breaklines]
void rw_abs_hd(int rw,unsigned int nr,unsigned int sec,unsigned int head,
	unsigned int cyl,struct buffer_head * bh)
{
        unsigned long lsec;
        lsec = sec + cyl * hd_info[nr].sect * head;
        rw_logical_hd(rw, nr, lsec, bh);
        bh->b_uptodate = 1;
}
\end{lstlisting}
\section{rw\_logical\_hd函数}
这个函数就是我们用来替代中断，读取硬盘扇区的新函数。首先定位根据逻辑扇区号计算出的偏移位置，然后读写一个block。
\begin{lstlisting}[breaklines]
void rw_logical_hd(int rw, int nr, unsigned long sec, struct buffer_head * bh)
{
        int c = 0;
        int dev = lHD[nr];

        printk("R/W sector 0x%x.\n\r", sec);

        if(lseek(dev,(sec-1)*512,SEEK_SET) >= 0)
        {
                if(rw == READ)
                        c = read(dev, bh->b_data, BLOCK_SIZE);
                else
                        c = write(dev, bh->b_data, BLOCK_SIZE);
        }
        else
        {
                panic("Read/Write Sector failed!\n");
        }
}
\end{lstlisting}
\section{hd\_init函数}
我们改写了这个函数，用来open磁盘镜像文件，并把文件句柄保存在lHD数组中。
\begin{lstlisting}[breaklines]
void hd_init(char **argv)
{
        int i,id;

        for (i=0 ; i<NR_HD ; i++) {
                hd[i*5].start_sect = 0;
                hd[i*5].nr_sects = hd_info[i].head*
                                hd_info[i].sect*hd_info[i].cyl;

                if ((id=open(argv[i+2],O_RDWR,0))<0)
                        panic("Unable to open 'diskimg'");

                lHD[i] = id;
        }
}
\end{lstlisting}
\section{sys\_install函数}
本函数先读取硬盘分区表，然后调用put\_super 函数在磁盘镜像文件创建super block，现在我们写死在ROOT\_DEV 分区中创建super block。
\begin{lstlisting}[breaklines]
void sys_install()
{
        int i,drive;
        struct partition *p;

        for (drive=0 ; drive<NR_HD ; drive++) {
                rw_abs_hd(READ,drive,1,0,0,(struct buffer_head *) start_buffer);
                if (!start_buffer->b_uptodate) {
                        printk("Unable to read partition table of drive %d\n\r",
                                drive);
                        panic("");
                }
                if (start_buffer->b_data[510] != 0x55 || (unsigned char)
                    start_buffer->b_data[511] != 0xAA) {
                        printk("Bad partition table on drive %d\n\r",drive);
                        panic("");
                }
                p = 0x1BE + (void *)start_buffer->b_data;
                for (i=1;i<5;i++,p++) {
                        hd[i+5*drive].start_sect = p->start_sect;
                        hd[i+5*drive].nr_sects = p->nr_sects;
                }
        }
        printk("Partition table%s ok.\n\r",(NR_HD>1)?"s":"");
        dump_partition(p);

        put_super();
}
\end{lstlisting}
\section{put\_super函数}
这个函数用来在磁盘镜像文件创建super block，然后创建根目录所在的inode。
\begin{lstlisting}[breaklines]
void put_super()
{
        struct super_block * p;
        struct buffer_head * bh;
        int drive = MINOR(ROOT_DEV) / 5;
        int block;
        struct m_inode * inode;
        struct dir_entry * de;
        int mode = 0;

        if (!(bh = bread(ROOT_DEV,1))) //`ROOT\_DEV的第一个block即为super block`
                return;
        p = (struct super_block *) (bh->b_data);
        p->s_ninodes = 8191; //`inode的数量`
        p->s_nzones = hd[drive].nr_sects / 2; //`数据块的数量`
        p->s_imap_blocks = p->s_ninodes / 8192 + 1; //`inode位图占用的块数`
        p->s_zmap_blocks = p->s_nzones / 8192 + 1; //`数据块位图占用的块数`
        p->s_firstdatazone = 1 + 1 //`第一个数据块的位置`
                + p->s_imap_blocks + p->s_zmap_blocks
                + p->s_ninodes / INODES_PER_BLOCK + 1;
        p->s_log_zone_size = 0;
        p->s_max_size = 0x10801c00;
        p->s_magic = 0x137f;
        bh->b_dirt = 1;

        block = 1;
        while((++block) <= p->s_firstdatazone){
                if (!(bh = bread(ROOT_DEV,block)))
                        panic("put_super: blk read error");
                memset(bh->b_data,0,BLOCK_SIZE);
                bh->b_dirt = 1;
                brelse(bh);
        } //`把所有数据块初始化`
        sys_sync(); //`写入硬盘`

        do_mount(ROOT_DEV); //`走初始化流程，加载创建好的super block`

        inode = new_inode(ROOT_DEV); //`为根目录申请inode`
        if (!inode) {
                panic("put_super: new inode fail.");
        }
        inode->i_size = 32;
        inode->i_dirt = 1;
        inode->i_mtime = inode->i_atime = CURRENT_TIME;

		//`为根目录申请数据块`
        if (!(inode->i_zone[0]=new_block(inode->i_dev)))
                panic("put_super: new block for ROOT_DIR fail.");
        if (!(bh=bread(inode->i_dev,inode->i_zone[0])))
                panic("put_super: read block for ROOT_DIR fail.");

        de = (struct dir_entry *) bh->b_data;
        de->inode=inode->i_num;
        strcpy(de->name,".");  //`根目录中的第一项内容`
        de++;
        de->inode = inode->i_num;
        strcpy(de->name,"..");  //`根目录中的第二项内容`
        inode->i_nlinks = 2;
        bh->b_dirt = 1;
        brelse(bh);
        inode->i_mode = I_DIRECTORY | (mode & 0777 & ~current->umask);
        iput(inode);

        sys_sync(); //`写入硬盘`
}
\end{lstlisting}
\section{sys\_ls函数}
这个函数用来列出磁盘镜像文件中指定的目录。
\begin{lstlisting}[breaklines]
void sys_ls(char *pathname)
{
        struct m_inode * inode;

        sys_setup(); //`装载文件系统`

        if (!(inode = get_dir(pathname)))
                panic("sys_ls: cannot find pathname.");
}
\end{lstlisting}
\section{sys\_adddir函数}
这个函数用来在磁盘镜像文件创建/dev目录。
\begin{lstlisting}[breaklines]
void sys_adddir()
{
        sys_setup();
        sys_mkdir("dev", 0);

        sys_sync();
}
\end{lstlisting}
\section{sys\_writefile函数}
这个函数用来在磁盘镜像文件复制指定的文件。
\begin{lstlisting}[breaklines]
void sys_writefile(char * pathname)
{
        int id, c, did;
        char buffer[BLOCK_SIZE];

        if ((id=open(pathname,O_RDWR,0))<0)
                panic("sys_writefile: Unable to open file");
        sys_setup();
        did = sys_creat(pathname, S_IFBLK);
        while(c = read(id, buffer, BLOCK_SIZE)){
                if(c==EOF)
                        return;
                else
                        sys_write(did, buffer, c);
        }
        sys_sync();
}
\end{lstlisting}
\section{sys\_readfile函数}
这个函数用来输出磁盘镜像文件指定的文本文件。
\begin{lstlisting}[breaklines]
void sys_readfile(char * pathname)
{
        int id, c;
        char buffer[BLOCK_SIZE];

        sys_setup();
        if(id = sys_open(pathname, O_RDWR, 0) < 0)
                panic("sys_readfile: Unable to open file");
        while(c = sys_read(id, buffer, BLOCK_SIZE)){
                if(c==EOF)
                        return;
                else
                        printk("%s", buffer);
        }
}
\end{lstlisting}
\chapter{块设备驱动程序源码block\_dev.c}
\section{主要全局变量}
\begin{lstlisting}[breaklines]
#include "buffer.h"
\end{lstlisting}
\section{block\_write函数}
直接继承，我们似乎用不到这个函数。
\begin{lstlisting}[breaklines]
int block_write(int dev, long * pos, char * buf, int count)
{
        int block = *pos / BLOCK_SIZE;
        int offset = *pos % BLOCK_SIZE;
        int chars;
        int written = 0;
        struct buffer_head * bh;
        register char * p;

        while (count>0) {
                bh = bread(dev,block);
                if (!bh)
                        return written?written:-EIO;
                chars = (count<BLOCK_SIZE) ? count : BLOCK_SIZE;
                p = offset + bh->b_data;
                offset = 0;
                block++;
                *pos += chars;
                written += chars;
                count -= chars;
                while (chars-->0)
                        *(p++) = get_fs_byte(buf++);
                bh->b_dirt = 1;
                brelse(bh);
        }
        return written;
}
\end{lstlisting}
\section{ll\_rw\_block函数}
改写，我们不再需要考虑访问冲突。
\begin{lstlisting}[breaklines]
void ll_rw_block(int rw, struct buffer_head * bh)
{
        rw_hd(rw, bh);
        bh->b_uptodate = 1;
}
\end{lstlisting}

\chapter{超级块处理源码supper.c}
\section{全局变量}
我们简化了头文件，这里需要额外增加一些外部的变量声明；
\begin{lstlisting}[breaklines]
#include "buffer.h"
static volatile int last_allocated_inode = 0;
extern struct m_inode inode_table[];
struct super_block super_block[NR_SUPER];
struct file file_table[NR_FILE];
extern struct task_struct * current;

/* set_bit uses setb, as gas doesn't recognize setc */
#define set_bit(bitnr,addr) ({ \
register int __res __asm__("ax"); \
__asm__("bt %2,%3;setb %%al":"=a" (__res):"a" (0),"r" (bitnr),"m" (*(addr))); \
__res; })
\end{lstlisting}
\section{do\_mount函数}
这个函数直接移植。首先在内存中的super block数组中找到一个空闲块，然后读取要挂载设备的1号逻辑块即super block；将读取到的super block内容复制到内存中，完成内存中super block的加载；然后按照s\_imap\_blocks 和s\_zmap\_blocks 指示读取对应的逻辑块。将imap和zmap对应的buffer block的最低位置1，表示已经占用。
\begin{lstlisting}[breaklines]
struct super_block * do_mount(int dev)
{
	struct super_block * p;
	struct buffer_head * bh;
	int i,block;

	for(p = &super_block[0] ; p < &super_block[NR_SUPER] ; p++ )
		if (!(p->s_dev))
			break;
	p->s_dev = -1;		/* mark it in use */
	if (p >= &super_block[NR_SUPER])
		return NULL;
	if (!(bh = bread(dev,1)))
		return NULL;
	*p = *((struct super_block *) bh->b_data);
	brelse(bh);
	if (p->s_magic != SUPER_MAGIC) {
		p->s_dev = 0;
		return NULL;
	}
	for (i=0;i<I_MAP_SLOTS;i++)
		p->s_imap[i] = NULL;
	for (i=0;i<Z_MAP_SLOTS;i++)
		p->s_zmap[i] = NULL;
	block=2; //`这里从2开始`
	for (i=0 ; i < p->s_imap_blocks ; i++)
		if (p->s_imap[i]=bread(dev,block))
			block++;
		else
			break;
	for (i=0 ; i < p->s_zmap_blocks ; i++)
		if (p->s_zmap[i]=bread(dev,block))
			block++;
		else
			break;
	if (block != 2+p->s_imap_blocks+p->s_zmap_blocks) {
		for(i=0;i<I_MAP_SLOTS;i++)
			brelse(p->s_imap[i]);
		for(i=0;i<Z_MAP_SLOTS;i++)
			brelse(p->s_zmap[i]);
		p->s_dev=0;
		return NULL;
	}
	p->s_imap[0]->b_data[0] |= 1;
	p->s_zmap[0]->b_data[0] |= 1;
	p->s_dev = dev;
	p->s_isup = NULL;
	p->s_imount = NULL;
	p->s_time = 0;
	p->s_rd_only = 0;
	p->s_dirt = 0;
	return p;
}
\end{lstlisting}
\section{mount\_root函数}
这个函数直接移植。然后挂载根设备并获得根设备上的1号inode；记录文件系统安装的inode和根目录的inode、在当前进程（初始进程）记录根目录和当前工作目录的inode；并根据位图统计硬盘上的空闲逻辑块数，和空闲inode数。
\begin{lstlisting}[breaklines]
void mount_root(void)
{
	int i,free;
	struct super_block * p;
	struct m_inode * mi;

	if (32 != sizeof (struct d_inode))
		panic("bad i-node size");
	for(i=0;i<NR_FILE;i++)
		file_table[i].f_count=0;
	for(p = &super_block[0] ; p < &super_block[NR_SUPER] ; p++)
		p->s_dev = 0;
	if (!(p=do_mount(ROOT_DEV)))
		panic("Unable to mount root");
	if (!(mi=iget(ROOT_DEV,1)))
		panic("Unable to read root i-node");
	mi->i_count += 3 ;	/* NOTE! it is logically used 4 times, not 1 */
	p->s_isup = p->s_imount = mi;
	current->pwd = mi;
	current->root = mi;
	free=0;
	i=p->s_nzones;
	while (-- i >= 0)
		if (!set_bit(i&8191,p->s_zmap[i>>13]->b_data))
			free++;
	printk("%d/%d free blocks\n\r",free,p->s_nzones);
	free=0;
	i=p->s_ninodes+1;
	while (-- i >= 0)
		if (!set_bit(i&8191,p->s_imap[i>>13]->b_data))
			free++;
	printk("%d/%d free inodes\n\r",free,p->s_ninodes);
}
\end{lstlisting}
\section{dump\_super函数}
新增函数。打印super block的内容。
\begin{lstlisting}[breaklines]
void dump_super(struct super_block * p)
{
        printk("s_ninodes 0x%x, s_nzones 0x%x.\n\r",
                        p->s_ninodes , p->s_nzones);
        printk("s_imap_blocks 0x%x, s_zmap_blocks 0x%x.\n\r",
                      p->s_imap_blocks, p->s_zmap_blocks);
        printk("s_firstdatazone 0x%x, s_log_zone_size 0x%x.\n\r",
                      p->s_firstdatazone, p->s_log_zone_size);
        printk("s_max_size 0x%x, s_magic 0x%x.\n\r",
                      p->s_max_size, p->s_magic);
}
\end{lstlisting}
\section{get\_super函数}
直接移植，获取对应设备的super block，原来该函数定义在fs.h中。
\begin{lstlisting}[breaklines]
extern inline struct super_block * get_super(int dev)
{
        struct super_block * s;

        for(s = 0+super_block;s < NR_SUPER+super_block; s++)
                if (s->s_dev == dev)
                        return s;
        return NULL;
}
\end{lstlisting}
\chapter{inode处理源码inode.c}
\section{全局变量}
简化了头文件；
\begin{lstlisting}[breaklines]
#include "buffer.h"
unsigned long CURRENT_TIME = 0;

struct m_inode inode_table[NR_INODE]={{0,},};

static void read_inode(struct m_inode * inode);
static void write_inode(struct m_inode * inode);
\end{lstlisting}
\section{wait\_on\_inode函数}
直接移植，我们实际上用不到等待和上锁操作；
\begin{lstlisting}[breaklines]
static inline void wait_on_inode(struct m_inode * inode)
{
	cli();
	while (inode->i_lock)
		sleep_on(&inode->i_wait);
	sti();
}

static inline void lock_inode(struct m_inode * inode)
{
	cli();
	while (inode->i_lock)
		sleep_on(&inode->i_wait);
	inode->i_lock=1;
	sti();
}

static inline void unlock_inode(struct m_inode * inode)
{
	inode->i_lock=0;
	wake_up(&inode->i_wait);
}
\end{lstlisting}
\section{sync\_inode函数}
直接移植；检查最多可以同时打开的NR\_INODE 个inode，如果有变化，写入硬盘inode对应的block。
\begin{lstlisting}[breaklines]
void sync_inodes(void)
{
	int i;
	struct m_inode * inode;

	inode = 0+inode_table;
	for(i=0 ; i<NR_INODE ; i++,inode++) {
		wait_on_inode(inode);
		if (inode->i_dirt && !inode->i_pipe)
			write_inode(inode);
	}
}
\end{lstlisting}
\section{\_bmap函数}
直接移植；把文件数据块映射到硬盘逻辑块。根据create的标志判断当硬盘逻辑块不存在时，是否新建。
\begin{lstlisting}[breaklines]
static int _bmap(struct m_inode * inode,int block,int create)
{
	struct buffer_head * bh;
	int i;

	if (block<0)
		panic("_bmap: block<0");
	if (block >= 7+512+512*512)  //`block不能大于直接块数+间接块数+二次间接块数`
		panic("_bmap: block>big");
	if (block<7) { //`直接块`
		if (create && !inode->i_zone[block])
			if (inode->i_zone[block]=new_block(inode->i_dev)) {
				inode->i_ctime=CURRENT_TIME;
				inode->i_dirt=1;
			}
		return inode->i_zone[block];
	}
	block -= 7; //`间接块`
	if (block<512) {
		if (create && !inode->i_zone[7])
			if (inode->i_zone[7]=new_block(inode->i_dev)) {
				inode->i_dirt=1;
				inode->i_ctime=CURRENT_TIME;
			}
		if (!inode->i_zone[7])
			return 0;
		if (!(bh = bread(inode->i_dev,inode->i_zone[7])))
			return 0;
		i = ((unsigned short *) (bh->b_data))[block];
		if (create && !i)
			if (i=new_block(inode->i_dev)) {
				((unsigned short *) (bh->b_data))[block]=i;
				bh->b_dirt=1;
			}
		brelse(bh);
		return i;
	}
	block -= 512; //`二次间接块`
	if (create && !inode->i_zone[8])
		if (inode->i_zone[8]=new_block(inode->i_dev)) {
			inode->i_dirt=1;
			inode->i_ctime=CURRENT_TIME;
		}
	if (!inode->i_zone[8])
		return 0;
	if (!(bh=bread(inode->i_dev,inode->i_zone[8])))
		return 0;
	i = ((unsigned short *)bh->b_data)[block>>9];
	if (create && !i)
		if (i=new_block(inode->i_dev)) {
			((unsigned short *) (bh->b_data))[block>>9]=i;
			bh->b_dirt=1;
		}
	brelse(bh);
	if (!i)
		return 0;
	if (!(bh=bread(inode->i_dev,i)))
		return 0;
	i = ((unsigned short *)bh->b_data)[block&511];
	if (create && !i)
		if (i=new_block(inode->i_dev)) {
			((unsigned short *) (bh->b_data))[block&511]=i;
			bh->b_dirt=1;
		}
	brelse(bh);
	return i;
}
\end{lstlisting}
\section{bmap函数}
直接移植；取文件block在硬盘上的逻辑block，如果对应的逻辑块不存在，就返回0.
\begin{lstlisting}[breaklines]
int bmap(struct m_inode * inode,int block)
{
	return _bmap(inode,block,0);
}
\end{lstlisting}
\section{create\_block函数}
直接移植；取文件block在硬盘上的逻辑block，如果对应的逻辑块不存在，就创建一块。
\begin{lstlisting}[breaklines]
int create_block(struct m_inode * inode, int block)
{
	return _bmap(inode,block,1);
}
\end{lstlisting}
\section{iput函数}
将修改后的inode写回硬盘，稍加改造，删除i\_pipe 类型的处理；
\begin{lstlisting}[breaklines]		
void iput(struct m_inode * inode)
{
	if (!inode)
		return;
	wait_on_inode(inode);
	if (!inode->i_count)
		panic("iput: trying to free free inode");
	if (inode->i_pipe) { //`我们现在用不到这种类型，删除相关的处理代码`
                panic("iput: pipe not support");	
	}
	if (!inode->i_dev || inode->i_count>1) {
		inode->i_count--;
		return;
	}
repeat:
	if (!inode->i_nlinks) {
		truncate(inode);
		free_inode(inode);
		return;
	}
	if (inode->i_dirt) {
		write_inode(inode);	/* we can sleep - so do again */
		wait_on_inode(inode);
		goto repeat;
	}
	inode->i_count--;
	return;
}
\end{lstlisting}
\section{get\_empty\_inode函数}
直接移植，在内存inode表中，找到一个空闲的inode；
\begin{lstlisting}[breaklines]		
static volatile int last_allocated_inode = 0;

struct m_inode * get_empty_inode(void)
{
	struct m_inode * inode;
	int inr;

	while (1) {
		inode = NULL;
		inr = last_allocated_inode;
		do {
			if (!inode_table[inr].i_count) {
				inode = inr + inode_table;
				break;
			}
			inr++;
			if (inr>=NR_INODE)
				inr=0;
		} while (inr != last_allocated_inode);
		if (!inode) {
			for (inr=0 ; inr<NR_INODE ; inr++)
				printk("%04x: %6d\t",inode_table[inr].i_dev,
					inode_table[inr].i_num);
			panic("No free inodes in mem");
		}
		last_allocated_inode = inr;
		wait_on_inode(inode);
		while (inode->i_dirt) {
			write_inode(inode);
			wait_on_inode(inode);
		}
		if (!inode->i_count)
			break;
	}
	memset(inode,0,sizeof(*inode));
	inode->i_count = 1;
	return inode;
}
\end{lstlisting}
\section{iget函数}
直接移植，根据设备号和inode的编号，读取硬盘上inode的内容；
\begin{lstlisting}[breaklines]		
struct m_inode * iget(int dev,int nr)
{
	struct m_inode * inode, * empty;

	if (!dev)
		panic("iget with dev==0");
	empty = get_empty_inode();
	inode = inode_table;
	while (inode < NR_INODE+inode_table) {
		if (inode->i_dev != dev || inode->i_num != nr) {
			inode++;
			continue;
		}
		wait_on_inode(inode);
		if (inode->i_dev != dev || inode->i_num != nr) {
			inode = inode_table;
			continue;
		}
		inode->i_count++;
		if (empty)
			iput(empty);
		return inode;
	}
	if (!empty)
		return (NULL);
	inode=empty;
	inode->i_dev = dev;
	inode->i_num = nr;
	read_inode(inode);
	return inode;
}
\end{lstlisting}
\section{read\_inode函数}
直接移植，读取硬盘上的inode的内容。
\begin{lstlisting}[breaklines]		
static void read_inode(struct m_inode * inode)
{
	struct super_block * sb;
	struct buffer_head * bh;
	int block;

	lock_inode(inode);
	sb=get_super(inode->i_dev);
	block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks +
		(inode->i_num-1)/INODES_PER_BLOCK;
	if (!(bh=bread(inode->i_dev,block)))
		panic("unable to read i-node block");
	*(struct d_inode *)inode =
		((struct d_inode *)bh->b_data)
			[(inode->i_num-1)%INODES_PER_BLOCK];
	brelse(bh);
	unlock_inode(inode);
}
\end{lstlisting}
\section{write\_inode函数}
直接移植，在创建inode时，对应的bitmap已经置位，这里只用处理inode的内容。
\begin{lstlisting}[breaklines]		
static void write_inode(struct m_inode * inode)
{
	struct super_block * sb;
	struct buffer_head * bh;
	int block;

	lock_inode(inode);
	sb=get_super(inode->i_dev);
	block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks +
		(inode->i_num-1)/INODES_PER_BLOCK;
	if (!(bh=bread(inode->i_dev,block)))
		panic("unable to read i-node block");
	((struct d_inode *)bh->b_data)
		[(inode->i_num-1)%INODES_PER_BLOCK] =
			*(struct d_inode *)inode;
	bh->b_dirt=1;
	inode->i_dirt=0;
	brelse(bh);
	unlock_inode(inode);
}
\end{lstlisting}
\chapter{inode和缓存位图管理程序bitmap.c}
\section{全局变量}
\begin{lstlisting}[breaklines]
* bitmap.c contains the code that handles the inode and block bitmaps */
#include "buffer.h"

extern unsigned long CURRENT_TIME;

#define clear_block(addr) \
__asm__("cld\n\t" \
        "rep\n\t" \
        "stosl" \
        ::"a" (0),"c" (BLOCK_SIZE/4),"D" ((long) (addr)))

#define set_bit(nr,addr) ({\
register int res __asm__("ax"); \
__asm__("btsl %2,%3\n\tsetb %%al":"=a" (res):"0" (0),"r" (nr),"m" (*(addr))); \
res;})

#define clear_bit(nr,addr) ({\
register int res __asm__("ax"); \
__asm__("btrl %2,%3\n\tsetnb %%al":"=a" (res):"0" (0),"r" (nr),"m" (*(addr))); \
res;})
\end{lstlisting}
\section{find\_first\_zero函数}
改写，原函数用嵌入式汇编编写，但执行结果似乎不对，我们用c改写。用于查找inode或block位图中第一个未使用的(即位图为0)。
\begin{lstlisting}[breaklines]
unsigned long find_first_zero(char * addr)
{
        unsigned long i, j, r;
        unsigned char c;
        unsigned char d;

        for(i=0;i<1000;i++){
                c = addr[i];
                for(j=0; j<8; j++){
                        d = ((c>>j) & 0x01);
                        if(d == 0){
                                r = i*8+j;
                                printk("find_first_zero: 0x%x\r\n", r);
                                return r;
                        }
                }
        }
        panic("find_first_zero: fail");
}
\end{lstlisting}
\section{free\_block函数}
直接继承，通过将uptodate位清零对内存中的缓存block置位，同时清楚bitmap位。
\begin{lstlisting}[breaklines]
void free_block(int dev, int block)
{
        struct super_block * sb;
        struct buffer_head * bh;

        if (!(sb = get_super(dev)))
                panic("trying to free block on nonexistent device");
        if (block < sb->s_firstdatazone || block >= sb->s_nzones)
                panic("trying to free block not in datazone");
        bh = get_hash_table(dev,block);
        if (bh) {
                if (bh->b_count != 1) {
                        printk("trying to free block (%04x:%d), count=%d\n",
                                dev,block,bh->b_count);
                        return;
                }
                bh->b_dirt=0;
                bh->b_uptodate=0;
                brelse(bh);
        }
        block -= sb->s_firstdatazone - 1 ;
        if (clear_bit(block&8191,sb->s_zmap[block/8192]->b_data)) {
                printk("block (%04x:%d) ",dev,block+sb->s_firstdatazone-1);
                panic("free_block: bit already cleared");
        }
        sb->s_zmap[block/8192]->b_dirt = 1;
}
\end{lstlisting}
\section{new\_block函数}
分配一个新的block，并将block的位图置位。
\begin{lstlisting}[breaklines]
int new_block(int dev)
{
        struct buffer_head * bh;
        struct super_block * sb;
        int i,j;

        if (!(sb = get_super(dev)))
                panic("trying to get new block from nonexistant device");
        j = 8192;
        for (i=0 ; i<8 ; i++)
                if (bh=sb->s_zmap[i]){
                        printk("new_block: map[%d][0] 0x%x\r\n",
                                        i, bh->b_data[0]);
                        if ((j=find_first_zero(bh->b_data))<8192)
                                break;
                }
        if (i>=8 || !bh || j>=8192)
                return 0;
        if (set_bit(j,bh->b_data)){
                printk("new_block: bit already set.\r\n");

                printk("new_block: map[%d][0] 0x%x\r\n",
                                        i, bh->b_data[0]);
                printk("new_block: %d \r\n", j);

//              bh->b_data[0] = 1;
                j=find_first_zero(bh->b_data);
                printk("new_block: map[%d][0] 0x%x\r\n",
                                        i, bh->b_data[0]);
                printk("new_block: %d \r\n", j);

                panic("new_block: bit already set");
        }
        bh->b_dirt = 1;
        j += i*8192 + sb->s_firstdatazone-1;
        if (j >= sb->s_nzones)
                return 0;
        printk("new_block: dev 0x%x, block 0x%x\r\n", dev, j);
        if (!(bh=getblk(dev,j)))
                panic("new_block: cannot get block");
        if (bh->b_count != 1){
                printk("new block: count is %d\r\n", bh->b_count);
                panic("new block: count != 1");
        }
        clear_block(bh->b_data);
        bh->b_uptodate = 1;
        bh->b_dirt = 1;
        brelse(bh);
        return j;
}
\end{lstlisting}
\section{free\_inode函数}
直接继承，释放inode，并将inode位图中对应bitmap置位
\begin{lstlisting}[breaklines]
void free_inode(struct m_inode * inode)
{
        struct super_block * sb;
        struct buffer_head * bh;

        if (!inode)
                return;
        if (!inode->i_dev) {
                memset(inode,0,sizeof(*inode));
                return;
        }
        if (inode->i_count>1) {
                printk("trying to free inode with count=%d\n",inode->i_count);
                panic("free_inode");
        }
        if (inode->i_nlinks)
                panic("trying to free inode with links");
        if (!(sb = get_super(inode->i_dev)))
                panic("trying to free inode on nonexistent device");
        if (inode->i_num < 1 || inode->i_num > sb->s_ninodes)
                panic("trying to free inode 0 or nonexistant inode");
        if (!(bh=sb->s_imap[inode->i_num>>13]))
                panic("nonexistent imap in superblock");
        if (clear_bit(inode->i_num&8191,bh->b_data))
                panic("free_inode: bit already cleared");
        bh->b_dirt = 1;
        memset(inode,0,sizeof(*inode));
}
\end{lstlisting}
\section{new\_inode函数}
申请inode，并将inode位图置位。
\begin{lstlisting}[breaklines]
struct m_inode * new_inode(int dev)
{
        struct m_inode * inode;
        struct super_block * sb;
        struct buffer_head * bh;
        int i,j;

        if (!(inode=get_empty_inode()))
                return NULL;
        if (!(sb = get_super(dev)))
                panic("new_inode with unknown device");
        j = 8192;
        for (i=0 ; i<8 ; i++)
                if (bh=sb->s_imap[i])
                        if ((j=find_first_zero(bh->b_data))<8192)
                                break;
        if (!bh || j >= 8192 || j+i*8192 > sb->s_ninodes) {
                printk("new_inode: j=%d, error\r\n", j);
                iput(inode);
                return NULL;
        }
        if (set_bit(j,bh->b_data))
                panic("new_inode: bit already set");
        bh->b_dirt = 1;
        inode->i_count=1;
        inode->i_nlinks=1;
        inode->i_dev=dev;
        inode->i_dirt=1;
        inode->i_num = j + i*8192;
        inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
        printk("create a new inode, %d\r\n", inode->i_num);
        return inode;
}
\end{lstlisting}
\chapter{文件或目录名处理源码namei.c}
\section{全局变量}
\begin{lstlisting}[breaklines]
#include "buffer.h"
extern struct task_struct *current;
extern unsigned long CURRENT_TIME;

#define MAY_EXEC 1
#define MAY_WRITE 2
#define MAY_READ 4
\end{lstlisting}
\section{permission函数}
直接移植，我们实际上不需要权限检查；
\begin{lstlisting}[breaklines]

/*
 *	permission()
 *
 * is used to check for read/write/execute permissions on a file.
 * I don't know if we should look at just the euid or both euid and
 * uid, but that should be easily changed.
 */
static int permission(struct m_inode * inode,int mask)
{
	int mode = inode->i_mode;

/* special case: not even root can read/write a deleted file */
	if (inode->i_dev && !inode->i_nlinks)
		return 0;
	if (!(current->uid && current->euid))
		mode=0777;
	else if (current->uid==inode->i_uid || current->euid==inode->i_uid)
		mode >>= 6;
	else if (current->gid==inode->i_gid || current->egid==inode->i_gid)
		mode >>= 3;
	return mode & mask & 0007;
}
\end{lstlisting}
\section{match函数}
这个函数需要改造，原来的match函数由于操作在内核态的操作空间，不能用strncmp比较用户空间传进来的name，需要设置fs段，指向用户态空间，在用户数据空间中进行比较；我们现在不需要了。
\begin{lstlisting}[breaklines]
/*
 * ok, we cannot use strncmp, as the name is not in our data space.
 * Thus we'll have to use match. No big problem. Match also makes
 * some sanity tests.
 *
 * NOTE! unlike strncmp, match returns 1 for success, 0 for failure.
 */
static int match(int len,const char * name,struct dir_entry * de)
{
        return (!strncmp(name, de->name, len));
}
\end{lstlisting}
\section{find\_entry函数}
直接移植，读取对应目录的inode的buffer block，遍历该block，匹配要查找的name；如果一个buffer block没有找到，遍历该inode的所有block。
\begin{lstlisting}[breaklines]
/*
 *	find_entry()
 *
 * finds and entry in the specified directory with the wanted name. It
 * returns the cache buffer in which the entry was found, and the entry
 * itself (as a parameter - res_dir). It does NOT read the inode of the
 * entry - you'll have to do that yourself if you want to.
 */
static struct buffer_head * find_entry(struct m_inode * dir,
	const char * name, int namelen, struct dir_entry ** res_dir)
{
	int entries;
	int block,i;
	struct buffer_head * bh;
	struct dir_entry * de;

#ifdef NO_TRUNCATE
	if (namelen > NAME_LEN)
		return NULL;
#else
	if (namelen > NAME_LEN)
		namelen = NAME_LEN;
#endif
	entries = dir->i_size / (sizeof (struct dir_entry));
	*res_dir = NULL;
	if (!namelen)
		return NULL;
	if (!(block = dir->i_zone[0])) //`存放目录内容的第一个block`
		return NULL;
	if (!(bh = bread(dir->i_dev,block)))
		return NULL;
	i = 0;
	de = (struct dir_entry *) bh->b_data;
	while (i < entries) {
		if ((char *)de >= BLOCK_SIZE+bh->b_data) {
			brelse(bh);
			bh = NULL; //`通过bmap函数，返回存放目录内容的下一个block`
			if (!(block = bmap(dir,i/DIR_ENTRIES_PER_BLOCK)) ||
			    !(bh = bread(dir->i_dev,block))) {
				i += DIR_ENTRIES_PER_BLOCK;
				continue;
			}
			de = (struct dir_entry *) bh->b_data;
		}
		if (match(namelen,name,de)) {
			*res_dir = de;
			return bh;
		}
		de++;
		i++;
	}
	brelse(bh);
	return NULL;
}
\end{lstlisting}
\section{add\_entry函数}
直接移植，读取对应目录的inode的buffer block，遍历该block，找到一个空的entry，填入name；
\begin{lstlisting}[breaklines]
/*
 *	add_entry()
 *
 * adds a file entry to the specified directory, using the same
 * semantics as find_entry(). It returns NULL if it failed.
 *
 * NOTE!! The inode part of 'de' is left at 0 - which means you
 * may not sleep between calling this and putting something into
 * the entry, as someone else might have used it while you slept.
 */
static struct buffer_head * add_entry(struct m_inode * dir,
	const char * name, int namelen, struct dir_entry ** res_dir)
{
	int block,i;
	struct buffer_head * bh;
	struct dir_entry * de;

	*res_dir = NULL;
#ifdef NO_TRUNCATE
	if (namelen > NAME_LEN)
		return NULL;
#else
	if (namelen > NAME_LEN)
		namelen = NAME_LEN;
#endif
	if (!namelen)
		return NULL;
	if (!(block = dir->i_zone[0])) //`目录inode对应第一个block`
		return NULL;
	if (!(bh = bread(dir->i_dev,block)))
		return NULL;
	i = 0;
	de = (struct dir_entry *) bh->b_data;
	while (1) {
		if ((char *)de >= BLOCK_SIZE+bh->b_data) {
			brelse(bh);
			bh = NULL; //`查找目录inode对应的block，无则分配一个block`
			block = create_block(dir,i/DIR_ENTRIES_PER_BLOCK);
			if (!block)
				return NULL;
			if (!(bh = bread(dir->i_dev,block))) {
				i += DIR_ENTRIES_PER_BLOCK;
				continue;
			}
			de = (struct dir_entry *) bh->b_data;
		} //`当前的目录表中没有空闲的entry，则创建一个新的entry`
		if (i*sizeof(struct dir_entry) >= dir->i_size) {
			de->inode=0;
			dir->i_size = (i+1)*sizeof(struct dir_entry);
			dir->i_dirt = 1;
			dir->i_ctime = CURRENT_TIME;
		}
		if (!de->inode) { //`找到空闲的entry，复制name`
			dir->i_mtime = CURRENT_TIME;
			for (i=0; i < NAME_LEN ; i++)
				de->name[i]=(i<namelen)?get_fs_byte(name+i):0;
			bh->b_dirt = 1;
			*res_dir = de;
			return bh;
		}
		de++;
		i++;
	}
	brelse(bh);
	return NULL;
}
\end{lstlisting}
\section{get\_dir函数}
直接移植，根据输入的pathname的第一个字符是不是‘/’判断是从root还是当前工作目录开始查找；在while循环中逐级遍历pathname中的每一层目录，最终找到非目录或者字符串终止为止。
\begin{lstlisting}[breaklines]
/*
 *	get_dir()
 *
 * Getdir traverses the pathname until it hits the topmost directory.
 * It returns NULL on failure.
 */
static struct m_inode * get_dir(const char * pathname)
{
	char c;
	const char * thisname;
	struct m_inode * inode;
	struct buffer_head * bh;
	int namelen,inr,idev;
	struct dir_entry * de;

	if (!current->root || !current->root->i_count)
		panic("No root inode");
	if (!current->pwd || !current->pwd->i_count)
		panic("No cwd inode");
	if ((c=get_fs_byte(pathname))=='/') {
		inode = current->root; //`从根目录开始`
		pathname++;
	} else if (c)
		inode = current->pwd; //`从当前目录开始`
	else
		return NULL;	/* empty name is bad */
	inode->i_count++;
	while (1) {
		thisname = pathname;
		if (!S_ISDIR(inode->i_mode) || !permission(inode,MAY_EXEC)) {
			iput(inode);
			return NULL;
		}
		for(namelen=0;(c=get_fs_byte(pathname++))&&(c!='/');namelen++)
			/* nothing */ ; //`pathname指针前移，指向下一级目录`
		if (!c) //`遍历pathname结束，返回的是最末一级目录的inode`
			return inode; 
		if (!(bh = find_entry(inode,thisname,namelen,&de))) {
			iput(inode); //`对于中间路径，找到对应的entry，找不到则出错`
			return NULL;
		}
		inr = de->inode;
		idev = inode->i_dev;
		brelse(bh);
		iput(inode);
		if (!(inode = iget(idev,inr))) //`根据entry信息，读取inode，找下一级`
			return NULL;
	}
}
\end{lstlisting}
\section{dir\_namei函数}
直接移植，通过get\_dir 得到最后一级文件或目录的inode；从路径名中提取出文件名和文件名的长度。
\begin{lstlisting}[breaklines]
/*
 *	dir_namei()
 *
 * dir_namei() returns the inode of the directory of the
 * specified name, and the name within that directory.
 */
static struct m_inode * dir_namei(const char * pathname,
	int * namelen, const char ** name)
{
	char c;
	const char * basename;
	struct m_inode * dir;

	if (!(dir = get_dir(pathname)))
		return NULL; //`找到最后一级目录或文件对应的inode`
	basename = pathname;
	while (c=get_fs_byte(pathname++))
		if (c=='/')
			basename=pathname;
	*namelen = pathname-basename-1;  //`目录名或文件名长度`
	*name = basename;  //`最后一级目录或文件名`
	return dir;
}
\end{lstlisting}
\section{namei函数}
直接移植，通过dir\_namei 得到最后一级目录所在的inode和文件名；通过对应的inode所在的buffer block里找到并返回文件名对应的inode；
\begin{lstlisting}[breaklines]
/*
 *	namei()
 *
 * is used by most simple commands to get the inode of a specified name.
 * Open, link etc use their own routines, but this is enough for things
 * like 'chmod' etc.
 */
struct m_inode * namei(const char * pathname)
{
	const char * basename;
	int inr,dev,namelen;
	struct m_inode * dir;
	struct buffer_head * bh;
	struct dir_entry * de;

	if (!(dir = dir_namei(pathname,&namelen,&basename)))
		return NULL;
	if (!namelen)			/* special case: '/usr/' etc */
		return dir;
	bh = find_entry(dir,basename,namelen,&de);
	if (!bh) {
		iput(dir);
		return NULL;
	}
	inr = de->inode;
	dev = dir->i_dev;
	brelse(bh);
	iput(dir);
	dir=iget(dev,inr);
	if (dir) {
		dir->i_atime=CURRENT_TIME;
		dir->i_dirt=1;
	}
	return dir;
}
\end{lstlisting}
\section{open\_namei函数}
直接移植，通过dir\_namei 得到最后一级目录所在的inode；如果找不到，则可能是新建，创建新的inode，否则，打开找到的inode，检查访问权限，更新访问时间。
\begin{lstlisting}[breaklines]
/*
 *	open_namei()
 *
 * namei for open - this is in fact almost the whole open-routine.
 */
int open_namei(const char * pathname, int flag, int mode,
	struct m_inode ** res_inode)
{
	const char * basename;
	int inr,dev,namelen;
	struct m_inode * dir, *inode;
	struct buffer_head * bh;
	struct dir_entry * de;

	if ((flag & O_TRUNC) && !(flag & O_ACCMODE))
		flag |= O_WRONLY;
	mode &= 0777 & ~current->umask;
	mode |= I_REGULAR;
	if (!(dir = dir_namei(pathname,&namelen,&basename)))
		return -ENOENT;
	if (!namelen) {			/* special case: '/usr/' etc */
		if (!(flag & (O_ACCMODE|O_CREAT|O_TRUNC))) {
			*res_inode=dir;
			return 0;
		}
		iput(dir);
		return -EISDIR;
	}
	bh = find_entry(dir,basename,namelen,&de);
	if (!bh) {
		if (!(flag & O_CREAT)) {
			iput(dir);
			return -ENOENT;
		}
		if (!permission(dir,MAY_WRITE)) {
			iput(dir);
			return -EACCES;
		}
		inode = new_inode(dir->i_dev);
		if (!inode) {
			iput(dir);
			return -ENOSPC;
		}
		inode->i_mode = mode;
		inode->i_dirt = 1;
		bh = add_entry(dir,basename,namelen,&de);
		if (!bh) {
			inode->i_nlinks--;
			iput(inode);
			iput(dir);
			return -ENOSPC;
		}
		de->inode = inode->i_num;
		bh->b_dirt = 1;
		brelse(bh);
		iput(dir);
		*res_inode = inode;
		return 0;
	}
	inr = de->inode;
	dev = dir->i_dev;
	brelse(bh);
	iput(dir);
	if (flag & O_EXCL)
		return -EEXIST;
	if (!(inode=iget(dev,inr)))
		return -EACCES;
	if ((S_ISDIR(inode->i_mode) && (flag & O_ACCMODE)) ||
	    permission(inode,ACC_MODE(flag))!=ACC_MODE(flag)) {
		iput(inode);
		return -EPERM;
	}
	inode->i_atime = CURRENT_TIME;
	if (flag & O_TRUNC)
		truncate(inode);
	*res_inode = inode;
	return 0;
}
\end{lstlisting}
\section{sys\_mkdir函数}
直接移植，创建一个目录，我们用这个函数建立一个空文件系统需要的dev等目录。
\begin{lstlisting}[breaklines]
int sys_mkdir(const char * pathname, int mode)
{
	const char * basename;
	int namelen;
	struct m_inode * dir, * inode;
	struct buffer_head * bh, *dir_block;
	struct dir_entry * de;

	if (current->euid && current->uid)
		return -EPERM;
	if (!(dir = dir_namei(pathname,&namelen,&basename)))
		return -ENOENT; //`找到要创建目录的上级目录`
	if (!namelen) {
		iput(dir);
		return -ENOENT;
	}
	if (!permission(dir,MAY_WRITE)) {
		iput(dir);
		return -EPERM;
	}
	bh = find_entry(dir,basename,namelen,&de);
	if (bh) {
		brelse(bh);
		iput(dir);
		return -EEXIST;
	}
	inode = new_inode(dir->i_dev); //`创建新的inode`
	if (!inode) {
		iput(dir);
		return -ENOSPC;
	}
	inode->i_size = 32;
	inode->i_dirt = 1;
	inode->i_mtime = inode->i_atime = CURRENT_TIME;
	if (!(inode->i_zone[0]=new_block(inode->i_dev))) {
		iput(dir); //`分配inode对应block`
		inode->i_nlinks--;
		iput(inode);
		return -ENOSPC;
	}
	inode->i_dirt = 1;
	if (!(dir_block=bread(inode->i_dev,inode->i_zone[0]))) {
		iput(dir);
		free_block(inode->i_dev,inode->i_zone[0]);
		inode->i_nlinks--;
		iput(inode);
		return -ERROR;
	}
	de = (struct dir_entry *) dir_block->b_data;
	de->inode=inode->i_num;
	strcpy(de->name,"."); //`创建目录entry.，指向自己`
	de++;
	de->inode = dir->i_num;
	strcpy(de->name,".."); //`创建目录entry..，指向父目录`
	inode->i_nlinks = 2;
	dir_block->b_dirt = 1;
	brelse(dir_block);
	inode->i_mode = I_DIRECTORY | (mode & 0777 & ~current->umask);
	inode->i_dirt = 1;
	bh = add_entry(dir,basename,namelen,&de); //`在父目录中增加entry`
	if (!bh) {
		iput(dir);
		free_block(inode->i_dev,inode->i_zone[0]);
		inode->i_nlinks=0;
		iput(inode);
		return -ENOSPC;
	}
	de->inode = inode->i_num;
	bh->b_dirt = 1;
	dir->i_nlinks++;
	dir->i_dirt = 1;
	iput(dir);
	iput(inode);
	brelse(bh);
	return 0;
}
\end{lstlisting}
\section{sys\_mknod函数}
从0.11中移植，创建一个设备文件，我们用这个函数来创建tty0
\begin{lstlisting}[breaklines]
int sys_mknod(const char * filename, int mode, int dev)
{
        const char * basename;
        int namelen;
        struct m_inode * dir, * inode;
        struct buffer_head * bh;
        struct dir_entry * de;

        if(!(dir = dir_namei(filename, &namelen, &basename)))
                return -ENOENT;
        if(!namelen){
                iput(dir);
                return -ENOENT;
        }

        bh = find_entry(&dir, basename, namelen, &de);
        if(bh){
                brelse(bh);
                iput(dir);
                return -EEXIST;
        }

        inode = new_inode(dir->i_dev);
        if(!inode){
                iput(dir);
                return -ENOSPC;
        }

        inode->i_mode = mode;
        if(S_ISBLK(mode) || S_ISCHR(mode))
                inode->i_zone[0] = dev; //`对于设备文件，zone中填写设备号`
        inode->i_mtime = inode->i_atime = CURRENT_TIME;
        inode->i_dirt = 1;

        bh = add_entry(dir, basename, namelen, &de);
        if(!bh){
                iput(dir);
                inode->i_nlinks=0;
                iput(inode);
                return -ENOSPC;
        }

        de->inode = inode->i_num;
        bh->b_dirt = 1;
        iput(dir);
        iput(inode);
        brelse(bh);
        sys_sync(); //`同步到硬盘上`
        return 0;
}
\end{lstlisting}
\chapter{文件打开操作源码open.c}
\section{全局变量}
\begin{lstlisting}[breaklines]
#include "buffer.h"
extern struct task_struct *current;
extern struct file file_table[NR_FILE];
\end{lstlisting}
\section{sys\_access函数}
直接移植，我们现在用不到这些函数。
\begin{lstlisting}[breaklines]
int sys_access(const char * filename,int mode)
{
	struct m_inode * inode;
	int res;

	mode &= 0007;
	if (!(inode=namei(filename)))
		return -EACCES;
	res = inode->i_mode & 0777;
	iput(inode);
	if (!(current->euid && current->uid))
		if (res & 0111)
			res = 0777;
		else
			res = 0666;
	if (current->euid == inode->i_uid)
		res >>= 6;
	else if (current->egid == inode->i_gid)
		res >>= 6;
	if ((res & 0007 & mode) == mode)
		return 0;
	return -EACCES;
}

int sys_chdir(const char * filename)
{
	struct m_inode * inode;

	if (!(inode = namei(filename)))
		return -ENOENT;
	if (!S_ISDIR(inode->i_mode)) {
		iput(inode);
		return -ENOTDIR;
	}
	iput(current->pwd);
	current->pwd = inode;
	return (0);
}

int sys_chroot(const char * filename)
{
	struct m_inode * inode;

	if (!(inode=namei(filename)))
		return -ENOENT;
	if (!S_ISDIR(inode->i_mode)) {
		iput(inode);
		return -ENOTDIR;
	}
	iput(current->root);
	current->root = inode;
	return (0);
}

int sys_chmod(const char * filename,int mode)
{
	struct m_inode * inode;

	if (!(inode=namei(filename)))
		return -ENOENT;
	if (current->uid && current->euid)
		if (current->uid!=inode->i_uid && current->euid!=inode->i_uid) {
			iput(inode);
			return -EACCES;
		} else 
			mode = (mode & 0777) | (inode->i_mode & 07000);
	inode->i_mode = (mode & 07777) | (inode->i_mode & ~07777);
	inode->i_dirt = 1;
	iput(inode);
	return 0;
}

int sys_chown(const char * filename,int uid,int gid)
{
	struct m_inode * inode;

	if (!(inode=namei(filename)))
		return -ENOENT;
	if (current->uid && current->euid) {
		iput(inode);
		return -EACCES;
	}
	inode->i_uid=uid;
	inode->i_gid=gid;
	inode->i_dirt=1;
	iput(inode);
	return 0;
}
\end{lstlisting}
\section{sys\_open函数}
稍加改造，删除了打开字符类设备的处理，sys\_open 作为open的入口函数，调用open\_namei ，找到文件对应的inode，并保存到进程的文件表中。
\begin{lstlisting}[breaklines]
int sys_open(const char * filename,int flag,int mode)
{
        struct m_inode * inode;
        struct file * f;
        int i,fd;

        mode &= 0777 & ~current->umask;
        for(fd=0 ; fd<NR_OPEN ; fd++)
                if (!current->filp[fd])
                        break;
        if (fd>=NR_OPEN)
                return -EINVAL;
        current->close_on_exec &= ~(1<<fd);
        f=0+file_table;
        for (i=0 ; i<NR_FILE ; i++,f++)
                if (!f->f_count) break;
        if (i>=NR_FILE)
                return -EINVAL;
        (current->filp[fd]=f)->f_count++;
        if ((i=open_namei(filename,flag,mode,&inode))<0) {
                current->filp[fd]=NULL;
                f->f_count=0;
                return i;
        }
/* ttys are somewhat special (ttyxx major==4, tty major==5) */
        if (S_ISCHR(inode->i_mode)) //`这里做了改造，我们不需要打开字符设备`
        	return -EPERM;
        f->f_mode = inode->i_mode;
        f->f_flags = flag;
        f->f_count = 1;
        f->f_inode = inode;
        f->f_pos = 0;
        printk("open file mode %06o\n\r", inode->i_mode);
        return (fd);
}
\end{lstlisting}
\section{sys\_creat函数}
直接移植，我们现在用不到这些函数；
\begin{lstlisting}[breaklines]
int sys_creat(const char * pathname, int mode)
{
	return sys_open(pathname, O_CREAT | O_TRUNC, mode);
}

int sys_close(unsigned int fd)
{	
	struct file * filp;

	if (fd >= NR_OPEN)
		return -EINVAL;
	current->close_on_exec &= ~(1<<fd);
	if (!(filp = current->filp[fd]))
		return -EINVAL;
	current->filp[fd] = NULL;
	if (filp->f_count == 0)
		panic("Close: file count is 0");
	if (--filp->f_count)
		return (0);
	iput(filp->f_inode);
	return (0);
}
\end{lstlisting}
\chapter{读写操作源码read\_write.c}
\section{全局变量}
\begin{lstlisting}[breaklines]
#include "buffer.h"
extern struct task_struct *current;
\end{lstlisting}
\section{sys\_read函数}
稍加改造，我们只读取常规类型的inode，调用file\_read 函数处理，且buffer为预先申请好的buf，不需要再做分页检查。
\begin{lstlisting}[breaklines]
int sys_read(unsigned int fd,char * buf,int count)
{
        struct file * file;
        struct m_inode * inode;

        if (fd>=NR_OPEN || count<0 || !(file=current->filp[fd]))
                return -EINVAL;
        if (!count)
                return 0;
        inode = file->f_inode;
        if (S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode)) {
                if (count+file->f_pos > inode->i_size)
                        count = inode->i_size - file->f_pos;
                if (count<=0)
                        return 0;
                return file_read(inode,file,buf,count);
        }
        printk("(Read)inode->i_mode=%06o\n\r",inode->i_mode);
        return -EINVAL;
}
\end{lstlisting}
\section{sys\_write函数}
稍加改造，我们只写常规类型的inode，调用file\_write 函数处理。
\begin{lstlisting}[breaklines]
int sys_write(unsigned int fd,char * buf,int count)
{
        struct file * file;
        struct m_inode * inode;

        if (fd>=NR_OPEN || count <0 || !(file=current->filp[fd]))
                return -EINVAL;
        if (!count)
                return 0;
        inode=file->f_inode;
        if (S_ISBLK(inode->i_mode))
                return block_write(inode->i_zone[0],&file->f_pos,buf,count);
        if (S_ISREG(inode->i_mode))
                return file_write(inode,file,buf,count);
        printk("(Write)inode->i_mode=%06o\n\r",inode->i_mode);
        return -EINVAL;
}
\end{lstlisting}
\chapter{文件驱动程序源码file\_dev.c}
\section{全局变量}
\begin{lstlisting}[breaklines]
#include "buffer.h"
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
\end{lstlisting}
\section{file\_read函数}
直接继承，先计算要访问的文件内容所在的block，读取对应block后再复制内容到buf。
\begin{lstlisting}[breaklines]
int file_read(struct m_inode * inode, struct file * filp, char * buf, int count)
{
        int left,chars,nr;
        struct buffer_head * bh;

        if ((left=count)<=0)
                return 0;
        while (left) { //`计算要访问的文件内容所在的block，读取对应block`
                if (nr = bmap(inode,(filp->f_pos)/BLOCK_SIZE)) {
                        if (!(bh=bread(inode->i_dev,nr)))
                                break;
                } else
                        bh = NULL;
                nr = filp->f_pos % BLOCK_SIZE;
                chars = MIN( BLOCK_SIZE-nr , left );
                filp->f_pos += chars;
                left -= chars;
                if (bh) { //`复制内容到buf`
                        char * p = nr + bh->b_data;
                        while (chars-->0)
                                put_fs_byte(*(p++),buf++);
                        brelse(bh);
                } else {
                        while (chars-->0)
                                put_fs_byte(0,buf++);
                }
        }
        inode->i_atime = CURRENT_TIME;
        return (count-left)?(count-left):-ERROR;
}
\end{lstlisting}
\section{file\_write函数}
\begin{lstlisting}[breaklines]
int file_write(struct m_inode * inode, struct file * filp, char * buf, int count)
{
        off_t pos;
        int block,c;
        struct buffer_head * bh;
        char * p;
        int i=0;

/*
 * ok, append may not work when many processes are writing at the same time
 * but so what. That way leads to madness anyway.
 */
        if (filp->f_flags & O_APPEND)
                pos = inode->i_size;
        else
                pos = filp->f_pos;
        while (i<count) { //`create block实际上是关联文件块到磁盘块，若不存在则创建`
                if (!(block = create_block(inode,pos/BLOCK_SIZE)))
                        break;
                if (!(bh=bread(inode->i_dev,block)))
                        break;
                c = pos % BLOCK_SIZE;
                p = c + bh->b_data;
                bh->b_dirt = 1;
                c = BLOCK_SIZE-c;
                if (c > count-i) c = count-i;
                pos += c;
                if (pos > inode->i_size) {
                        inode->i_size = pos;
                        inode->i_dirt = 1;
                }
                i += c;
                while (c-->0)
                        *(p++) = get_fs_byte(buf++);
                brelse(bh);
        }
        inode->i_mtime = CURRENT_TIME;
        if (!(filp->f_flags & O_APPEND)) {
                filp->f_pos = pos;
                inode->i_ctime = CURRENT_TIME;
        }
        return (i?i:-1);
}      
\end{lstlisting}
\chapter{日志打印输出源码printk.c}
\section{printk函数}
改造，将原来的内核日志输出，转换成标准的输入输出
\begin{lstlisting}[breaklines]
#include <stdarg.h>
#include <stdio.h>

int printk(const char *fmt, ...)
{
        char buffer [1024];
        va_list args;
        va_start(args, fmt);
        vsprintf(buffer,fmt,args);
        va_end(args);
        fprintf(stderr, buffer);
        return 0;
}
\end{lstlisting}
\chapter{异常处理源码panic.c}
\section{panic函数}
直接移植。
\begin{lstlisting}[breaklines]
volatile void panic(const char * s)
{
        printk("Kernel panic: %s\n\r",s);
        for(;;);
}
\end{lstlisting}

\chapter{总头文件buffer.h}
\begin{lstlisting}[breaklines]
define HD_TYPE { 5,17,980,300,980,0 },{ 5,17,980,300,980,0 }
#define NR_HASH 307
#define NR_OPEN 20
#define NULL ((void *) 0)
#define READ 0
#define WRITE 1
#define BLOCK_SIZE 1024

typedef int (*fn_ptr)();

struct i387_struct {
        long    cwd;
        long    swd;
        long    twd;
        long    fip;
        long    fcs;
        long    foo;
        long    fos;
        long    st_space[20];   /* 8*10 bytes for each FP-reg = 80 bytes */
};

typedef struct desc_struct {
        unsigned long a,b;
};

struct tss_struct {
        long    back_link;      /* 16 high bits zero */
        long    esp0;
        long    ss0;            /* 16 high bits zero */
        long    esp1;
        long    ss1;            /* 16 high bits zero */
        long    esp2;
        long    ss2;            /* 16 high bits zero */
        long    cr3;
        long    eip;
        long    eflags;
        long    eax,ecx,edx,ebx;
        long    esp;
        long    ebp;
        long    esi;
        long    edi;
        long    es;             /* 16 high bits zero */
        long    cs;             /* 16 high bits zero */
        long    ss;             /* 16 high bits zero */
        long    ds;             /* 16 high bits zero */
        long    fs;             /* 16 high bits zero */
        long    gs;             /* 16 high bits zero */
        long    ldt;            /* 16 high bits zero */
        long    trace_bitmap;   /* bits: trace 0, bitmap 16-31 */
        struct i387_struct i387;
};
struct task_struct {
/* these are hardcoded - don't touch */
        long state;     /* -1 unrunnable, 0 runnable, >0 stopped */
        long counter;
        long priority;
        long signal;
        fn_ptr sig_restorer;
        fn_ptr sig_fn[32];
/* various fields */
        int exit_code;
        unsigned long end_code,end_data,brk,start_stack;
        long pid,father,pgrp,session,leader;
        unsigned short uid,euid,suid;
        unsigned short gid,egid,sgid;
        long alarm;
        long utime,stime,cutime,cstime,start_time;
        unsigned short used_math;
/* file system info */
        int tty;                /* -1 if no tty, so it must be signed */
        unsigned short umask;
        struct m_inode * pwd;
        struct m_inode * root;
        unsigned long close_on_exec;
        struct file * filp[NR_OPEN];
/* ldt for this task 0 - zero 1 - cs 2 - ds&ss */
        struct desc_struct ldt[3];
/* tss for this task */
        struct tss_struct tss;
};
struct buffer_head {
        char * b_data;                  /* pointer to data block (1024 bytes) */
        unsigned short b_dev;           /* device (0 = free) */
        unsigned short b_blocknr;       /* block number */
        unsigned char b_uptodate;
        unsigned char b_dirt;           /* 0-clean,1-dirty */
        unsigned char b_count;          /* users using this block */
        unsigned char b_lock;           /* 0 - ok, 1 -locked */
        struct task_struct * b_wait;
        struct buffer_head * b_prev;
        struct buffer_head * b_next;
        struct buffer_head * b_prev_free;
        struct buffer_head * b_next_free;
};

typedef long off_t;
struct file {
        unsigned short f_mode;
        unsigned short f_flags;
        unsigned short f_count;
        struct m_inode * f_inode;
        off_t f_pos;
};

struct m_inode {
        unsigned short i_mode;
        unsigned short i_uid;
        unsigned long i_size;
        unsigned long i_mtime;
        unsigned char i_gid;
        unsigned char i_nlinks;
        unsigned short i_zone[9];
/* these are in memory also */
        struct task_struct * i_wait;
        unsigned long i_atime;
        unsigned long i_ctime;
        unsigned short i_dev;
        unsigned short i_num;
        unsigned short i_count;
        unsigned char i_lock;
        unsigned char i_dirt;
        unsigned char i_pipe;
        unsigned char i_mount;
        unsigned char i_seek;
        unsigned char i_update;
};
struct partition {
        unsigned char boot_ind;         /* 0x80 - active (unused) */
        unsigned char head;             /* ? */
        unsigned char sector;           /* ? */
        unsigned char cyl;              /* ? */
        unsigned char sys_ind;          /* ? */
        unsigned char end_head;         /* ? */
        unsigned char end_sector;       /* ? */
        unsigned char end_cyl;          /* ? */
        unsigned int start_sect;        /* starting sector counting from 0 */
        unsigned int nr_sects;          /* nr of sectors in partition */
};

struct d_inode {
        unsigned short i_mode;
        unsigned short i_uid;
        unsigned long i_size;
        unsigned long i_time;
        unsigned char i_gid;
        unsigned char i_nlinks;
        unsigned short i_zone[9];
};
struct super_block {
        unsigned short s_ninodes;
        unsigned short s_nzones;
        unsigned short s_imap_blocks;
        unsigned short s_zmap_blocks;
        unsigned short s_firstdatazone;
        unsigned short s_log_zone_size;
        unsigned long s_max_size;
        unsigned short s_magic;
/* These are only in memory */
        struct buffer_head * s_imap[8];
        struct buffer_head * s_zmap[8];
        unsigned short s_dev;
        struct m_inode * s_isup;
        struct m_inode * s_imount;
        unsigned long s_time;
        unsigned char s_rd_only;
        unsigned char s_dirt;
};
#define NAME_LEN 14
struct dir_entry {
        unsigned short inode;
        char name[NAME_LEN];
};
#define NR_SUPER 8
#define INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct d_inode)))
#define DIR_ENTRIES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct dir_entry)))
#define NR_INODE 32
#define NR_FILE 64

#define S_IFMT  00170000
#define S_IFREG  0100000
#define S_IFBLK  0060000
#define S_IFDIR  0040000
#define S_IFCHR  0020000
#define S_IFIFO  0010000
#define S_ISUID  0004000
#define S_ISGID  0002000
#define S_ISVTX  0001000

#define S_ISREG(m)      (((m) & S_IFMT) == S_IFREG)
#define S_ISDIR(m)      (((m) & S_IFMT) == S_IFDIR)
#define S_ISCHR(m)      (((m) & S_IFMT) == S_IFCHR)
#define S_ISBLK(m)      (((m) & S_IFMT) == S_IFBLK)
#define S_ISFIFO(m)     (((m) & S_IFMT) == S_IFIFO)

#define MAJOR(a) (((unsigned)(a))>>8)
#define MINOR(a) ((a)&0xff)

#define NAME_LEN 14

#define I_MAP_SLOTS 8
#define Z_MAP_SLOTS 8
#define SUPER_MAGIC 0x137F

#define ROOT_DEV 0x301

#define sleep_on(a) 
#define wake_up(a) 
#define cli()
#define sti()
#define ERROR           99
#define EPERM            1
#define ENOENT           2
#define EIO              5
#define EACCES          13
#define EEXIST          17
#define ENOTDIR         20
#define EISDIR          21
#define EINVAL          22
#define ENOSPC          28

#define I_TYPE          0170000
#define I_DIRECTORY     0040000
#define I_REGULAR       0100000
#define I_BLOCK_SPECIAL 0060000
#define I_CHAR_SPECIAL  0020000
#define I_NAMED_PIPE    0010000
#define I_SET_UID_BIT   0004000
#define I_SET_GID_BIT   0002000

#define get_fs_byte(a) (*((char *)(a)))
#define put_fs_byte(a, b) ((*((char *)b))=(a))

#define O_ACCMODE       00003
#define O_RDONLY           00
#define O_WRONLY           01
#define O_RDWR             02
#define O_CREAT         00100   /* not fcntl */
#define O_EXCL          00200   /* not fcntl */
#define O_NOCTTY        00400   /* not fcntl */
#define O_TRUNC         01000   /* not fcntl */
#define O_APPEND        02000
#define O_NONBLOCK      04000   /* not fcntl */

#define ACC_MODE(x) ("\004\002\006\377"[(x)&O_ACCMODE])

extern unsigned long CURRENT_TIME;

\end{lstlisting}

\chapter{模拟运行}
\section{准备空白硬盘映像文件}
\subsection{硬盘参数}
在include/linux/config.h文件中，我们可以看到对硬盘参数的定义，我们看到在定义了LINUS\_HD 宏的情况下，我们需要两个参数相同的硬盘，硬盘参数是（5,17,980,300,980,0）
\begin{lstlisting}[breaklines]
#if	defined(LASU_HD)
#define HD_TYPE { 7,35,915,65536,920,0 }
#elif	defined(LINUS_HD)
#define HD_TYPE { 5,17,980,300,980,0 },{ 5,17,980,300,980,0 }
\end{lstlisting}
在kernel/hd.c文件中，我们可以看到几个硬盘参数分别对应含义
\begin{lstlisting}[breaklines]
static struct hd_i_struct{
	int head,sect,cyl,wpcom,lzone,ctl;
	} hd_info[]= { HD_TYPE };
\end{lstlisting}
即，head = 5, sect = 17， cyl = 980，5个磁头，980个柱面，每个柱面17个扇区。
\subsection{生成硬盘映像}
根据硬盘参数，我们可以计算得到硬盘的扇区数： 5 * 17 * 980 = 83300
\begin{lstlisting}[breaklines]
dd if=/dev/zero of=hdc.img bs=512 count=83300
\end{lstlisting}
\section{格式化硬盘映像}
我们使用fdisk命令格式化硬盘，其中-c=dos -u=cylinders指示fdisk使用dos兼容模式，并使用cylinders作为单位。
\begin{lstlisting}[breaklines]
fdisk -c=dos -u=cylinders hdc.img
`欢迎使用 fdisk (util-linux 2.34)。`
`更改将停留在内存中，直到您决定将更改写入磁盘。`
`使用写入命令前请三思。`

`设备不包含可识别的分区表。`
`DOS 兼容模式已废弃。`
`以柱面为显示单位的方式已废弃。`

`创建了一个磁盘标识符为 0x27efac31 的新 DOS 磁盘标签。`

`命令(输入 m 获取帮助)： `
\end{lstlisting}
我们要设置硬盘的head、sect、cyl参数，需要进入专家模式，键入'x'
\begin{lstlisting}[breaklines]
`命令(输入 m 获取帮助)： x`

`专家命令(输入 m 显示帮助)： c`
`柱面数 (1-1048576, 默认 5): 980`

`专家命令(输入 m 显示帮助)： h`
`磁头数 (1-255, 默认 255): 5`

`专家命令(输入 m 显示帮助)： s`
`扇区数 (1-63, 默认 63): 17`

`专家命令(输入 m 显示帮助)： r`
\end{lstlisting}
接下来创建一个主分区
\begin{lstlisting}[breaklines]
`命令(输入 m 获取帮助)： n`
`分区类型`
`   p   主分区 (0个主分区，0个扩展分区，4空闲)`
`   e   扩展分区 (逻辑分区容器)`
`选择 (默认 p)： p`
`分区号 (1-4, 默认  1): `
`第一个柱面 (1-980, 默认 1): `
`Last cylinder, +/-cylinders or +/-size{K,M,G,T,P} (1-980, 默认 980): `

`创建了一个新分区 1，类型为“Linux”，大小为 40.7 MiB。`
\end{lstlisting}
修改分区类型，并保存退出
\begin{lstlisting}[breaklines]
`命令(输入 m 获取帮助)： t`
`已选择分区 1`
`Hex 代码(输入 L 列出所有代码)： 0x81`
`已将分区“Linux”的类型更改为“Minix / old Linux”。`
`命令(输入 m 获取帮助)： w`
`分区表已调整。`
`正在同步磁盘。`
\end{lstlisting}
\section{在磁盘镜像上安装文件系统}
\paragraph{1} 在硬盘镜像上构建supper block和根目录/ \\
m2 1 硬盘镜像文件名
\paragraph{2} 在硬盘镜像上根目录/下创建dev目录 \\
m2 3 硬盘镜像文件名
\paragraph{3} 在硬盘镜像上/dev目录下创建设备文件tty0 \\
m2 4 硬盘镜像文件名
\section{验证}
用生成的硬盘镜像文件替代我们在上一章使用的手工构造的磁盘镜像，应该可以得到和一章相同的输出结果。

\end{CJK}
\end{document}
